package com.yinxing.netty.server;

import com.yinxing.framework.utils.SpringUtils;
import com.yinxing.webapi.config.AppConfig;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import static io.netty.channel.ChannelOption.*;

@Slf4j
@Component
@DependsOn({SpringUtils.CLASSNAME})
public class NettyServer {

    private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    private final EventLoopGroup workerGroup = new NioEventLoopGroup();
    private final String localAddress = "0.0.0.0";
    private int nettyServerPort;
    private Channel serverChannel;

    @PostConstruct
    public void start() throws Exception {
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
            .channel(NioServerSocketChannel.class)
            .handler(new LoggingHandler(LogLevel.DEBUG))
            //默认值128,三次握手过程中的链接队列长度,如果存在并发链接可以增大该值.
            .option(SO_BACKLOG, 1024)
            //关闭tcp协议层健康检查.
            .childOption(SO_KEEPALIVE, false)
            //堆外内存池分配器.
            .childOption(ALLOCATOR, PooledByteBufAllocator.DEFAULT)
            //申请内存大小计算器:(64,1024,65536)范围内自动调整ByteBuf申请字节大小.
            .childOption(RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, 1024, 65536))
            //高低水位:写入缓冲区队列大小5M~10M.
            .childOption(WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(5*1024*1024, 10*1024*1024))
            //如果设置为false,tcp协议层会合并小消息,会导致大约40毫秒的延迟.
            //如果是延迟敏感型的程序,需要设置为true.
            .childOption(TCP_NODELAY, false)
            .childOption(SO_REUSEADDR, true)
            //单个socket的读写缓冲区.
            .childOption(SO_SNDBUF, 8*1024)
            .childOption(SO_RCVBUF, 8*1024)
            .childHandler(new ServerInitializer());
        this.nettyServerPort = AppConfig.getInstance().getNettyPort();
        this.serverChannel = b.bind(localAddress, nettyServerPort).sync().channel();
        log.info("Netty Server started on port: {}", nettyServerPort);
    }

    @PreDestroy
    public void stop() {
        this.serverChannel.close();
        this.bossGroup.shutdownGracefully();
        this.workerGroup.shutdownGracefully();
        log.info("Netty Server stopped on port: {}", nettyServerPort);
    }
}

